/*
 * ---------------- www.spacesimulator.net --------------
 *   ---- Space simulators and 3d engine tutorials ----
 *
 * Author: Damiano Vitulli
 *
 * This program is released under the BSD licence
 * By using this program you agree to licence terms on spacesimulator.net copyright page
 *
 *
 * INI file loader
 *  
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
#include <stdlib.h>
#include "load_ini.h"


 
/*
 * VARIABLES DECLARATION
 *
 */

int loadini_dictionaries_qty=0;
loadini_type loadini[LOADINI_MAX_DICTIONARIES];



/*
 * int LoadINI(char *p_filename)
 *
 * This function loads an ini file and fills a loadini_type array
 *
 * Input parameters: p_filename = Filename of the ini file to load
 *
 * Return value: (char) >0 if the ini file was loaded correctly, 0 otherwise
 *
 */

int LoadINI(char *p_filename) 
{
    FILE *l_file; // File pointer
	char l_string[LOADINI_MAX_CHARS]; // it contains section,key and value
	char l_section[LOADINI_MAX_CHARS]; // section
	char l_key[LOADINI_MAX_CHARS]; // key
	char l_value[LOADINI_MAX_CHARS]; // value
	unsigned int l_entries_qty=0; // Number of entries loaded

 	if (p_filename=='\0') return(0);
    fprintf(stdout, "Loading INI file: %s\nDone",p_filename);

	if((l_file = fopen(p_filename, "r"))==NULL) 
	{
	    fprintf( stdout, "File %s not found!\nFailed",p_filename);
		return (0);
	}
	else
	{
		sprintf(loadini[loadini_dictionaries_qty].filename,"%s",p_filename); // store the filename into the dictionary structure
	}
	
	// Now we start a loop to scan the whole file
	while (fgets(l_string, LOADINI_MAX_CHARS-1, l_file)!=NULL) // Read the line
	{
		if(strlen(l_string)>1)
			LoadINI_StrDelSpaces (l_string); // Remove all the spaces before and after the loaded string
		strlwr(l_string); // Put all the characters in lower case 
		switch (l_string[0]) // Check the first character
		{
		case ';': case '#': case '\0': case 13: case 10:
			// Comments or blank lines! Do nothing...
		break;
		case '[':
			// Reading section...
			if (sscanf(l_string, "[%[^]]", l_section)==1) // Read section
			{
				LoadINI_StrDelSpaces (l_section); // Remove all the spaces before and after the loaded string
			}
		break;
		default:
			// Reading key and value...
			LoadINI_StrDelComments(l_string); // Remove comments after the string
			if (LoadINI_KeyParser(l_string,l_key,l_value)) // Read key and value
			{				
				LoadINI_AddField(loadini_dictionaries_qty,l_entries_qty,l_section,l_key,l_value); // Let's insert now Section,Key and Value into the dictionary
				l_entries_qty++; // Increase the number of items
			}
		break;
		}
	}
	
	loadini[loadini_dictionaries_qty].entries_qty=l_entries_qty;
	loadini_dictionaries_qty++; // Increase the number of dictionaries
	return(1);
	fclose(l_file);
}



/*
 * int LoadINI_Save(char *p_filename)
 *
 * This function saves an ini file
 *
 * Input parameters: p_filename = Filename of the ini file to save
 *
 * Return value: (char) >0 if the ini file was saved correctly, 0 otherwise
 *
 */

int LoadINI_Save(char *p_filename)
{
    int i,j; //Index variables
	unsigned int l_entries_qty=0;
    FILE *l_file; //File pointer
	char l_section[LOADINI_MAX_CHARS+3];
	char l_key_and_value[LOADINI_MAX_CHARS*2+2];
	char l_last_section[LOADINI_MAX_CHARS+3];

 	if (p_filename=='\0') return(0);
    fprintf(stdout, "Saving INI file: %s\n",p_filename);

	if((l_file = fopen(p_filename, "w"))==NULL) 
	{
	    fprintf( stdout, "Could not open file %s\n",p_filename);
		return (0);
	}
	
	l_section[0]='\0';
	fputs("#Ini file generated by Zetadeck modular 3d engine - spacesimulator.net\n",l_file);
	// Check all the dictionaries to find the correct filename
	for (i=0;i<loadini_dictionaries_qty;i++)
	{
		if (!strcmp(p_filename,loadini[i].filename)) // If the filename is correct
		{
			for (j=0;j<loadini[i].entries_qty;j++) // For each dictionary element
			{
				// Let's now save the Section (where needed),Key and Value
				sprintf(l_section,"\n[%s]\n",loadini[i].fields[j].section);
				sprintf(l_key_and_value,"%s=%s\n",loadini[i].fields[j].key,loadini[i].fields[j].value);
				if (strcmp(l_section,l_last_section)) // If the section is different from the previous
				{
					fputs(l_section,l_file); // Save the section
					strcpy(l_last_section,l_section);
				}
				fputs(l_key_and_value,l_file); // Save key and value
			}
		}
	}
	return(1);
}



/*
 * int LoadINI_KeyParser(char *p_source_string,char *p_key, char *p_value)
 *
 * This function extrapolates key and value from a given string (ex. value=100)
 *
 * Input parameters: *p_source_string = pointer of the string to parse
 *                   *p_key = pointer to the key (return)
 *                   *p_value = pointer to the value (return)
 *
 * Return value: (char) >0 if the string was parsed correctly, 0 otherwise
 *
 */

int LoadINI_KeyParser(char *p_source_string,char *p_key, char *p_value)
{
	char *l_ptr;
	p_key[0]='\0';
	p_value[0]='\0';

	l_ptr=strchr(p_source_string,'='); // find the position of the "=" 
	if (l_ptr && (l_ptr-p_source_string)!=(int)strlen(p_source_string)-1) // if the "=" is found and there is value on the right
	{
		strncpy(p_key,p_source_string,l_ptr-p_source_string);  // extrapolate the key
		p_key[l_ptr-p_source_string]='\0';
		strcpy(p_value,l_ptr+1); // extrapolate the value
		LoadINI_StrDelSpaces (p_key); // remove space before and after the key
		LoadINI_StrDelSpaces (p_value);  // remove space before and after the value
		LoadINI_StrDelChars (p_value,"\"\'"); // remove unwanted chars
		return (1);
	}
	else
	{
		return (0);
	}
}



/*
 * void LoadINI_AddField(int p_dictionary_number, int p_field_number, char *p_section, char *p_key, char *p_value)
 *
 * This function re-allocates the memory and insert a new dictionary value into the p_field_number position
 *
 * Input parameters: p_dictionary_number = number of the dictionary (array)
 *                   p_field_number = number of the field (array position)
 *					 *p_section = section
 *                   *p_key = key 
 *                   *p_value = value
 *
 */

void LoadINI_AddField(int p_dictionary_number, int p_field_number, char *p_section, char *p_key, char *p_value)
{
    // Allocate the memory for the new item
	loadini[p_dictionary_number].fields[p_field_number].section=(char *)realloc(loadini[p_dictionary_number].fields[p_field_number].section,strlen(p_section));
	loadini[p_dictionary_number].fields[p_field_number].key=(char *)realloc(loadini[p_dictionary_number].fields[p_field_number].key,strlen(p_key));
	loadini[p_dictionary_number].fields[p_field_number].value=(char *)realloc(loadini[p_dictionary_number].fields[p_field_number].value,strlen(p_value));

	// Create the new item
	strcpy(loadini[p_dictionary_number].fields[p_field_number].section,p_section);
	strcpy(loadini[p_dictionary_number].fields[p_field_number].key,p_key);
	strcpy(loadini[p_dictionary_number].fields[p_field_number].value,p_value);
}



/*
 * void LoadINI_StrDelChars (char *p_string, char *p_char_list)
 *
 * This function removes the characters specified in p_char_list from p_string
 *
 * Input parameters: *p_string = source/destination string 
 *					 *p_char_list = null terminated array of characters
 *
 */

void LoadINI_StrDelChars (char *p_string, char *p_char_list)
{
	unsigned int i,j,k=0;
	bool l_found=false; // Boolean flag to check if a character is found
	char l_string[LOADINI_MAX_CHARS]; 

	for (i=0;i<strlen(p_string);i++)
	{
		l_found=false; // Set the flag to false
		for (j=0;j<strlen(p_char_list);j++)
		{
			if (p_string[i]==p_char_list[j])
			{
				l_found=true; // If one of the chars in list is found set the flag to true
			}
		}
		if (l_found==false)
		{
			l_string[k]=p_string[i]; // If the previous character was not in the "black list" then copy it into the return string
			k++;
		}
	}
	l_string[k]='\0'; // Append the terminator to the return string
	strcpy(p_string,l_string);
}



/*
 * void LoadINI_StrDelSpaces(char *p_string)
 *
 * This subroutine removes spaces before and after a string
 *
 * Input parameters: *p_string = source/destination string
 *
 */

void LoadINI_StrDelSpaces(char *p_string)
{
    unsigned int i;
	char l_string[LOADINI_MAX_CHARS];

	//Remove spaces before string
	for (i=0;i<strlen(p_string);i++)
		if (!isspace((int)p_string[i])) // When the char is not a space...
		{
			strcpy(l_string,&p_string[i]); // ...move the pointer to that address
			break;
		}
	//Remove spaces after the string
	for (i=strlen(p_string)-1;i>=0;i--) // Scan from right side
		if (!isspace((int)p_string[i])) // When the char is not a space...
		{
			strcpy(l_string,p_string); // ...copy the string...
			l_string[i+1]='\0'; // ...and put a terminator to end the string
			break;
		}
	strcpy(p_string,l_string);
}



/*
 * void LoadINI_StrDelComments(char *p_string)
 *
 * This subroutine removes the .ini comments (strings that starts with ";" or "#") from a string
 *
 * Input parameters: *p_string = source/destination string
 *
 */

void LoadINI_StrDelComments(char *p_string)
{
    unsigned int i;

	//Remove comments after the string
	for (i=0; i<strlen(p_string);i++)
	if (p_string[i]==';' || p_string[i]=='#') // When there is a comment identifier...
	{
		p_string[i]='\0'; // ...put a terminator to end the string
		break;
	}
}



/*
 * char *LoadINI_GetString(char* p_filename, char *p_section, char *p_key, char *p_default)
 *
 * This function gets a string value from an ini file (that must be already loaded with the function LoadINI)
 *
 * Input parameters: *p_filename = Filename of the ini file already loaded
 *                   *p_section = section
 *                   *p_key = key
 *                   *p_default = default value to return if section and key were not found
 *
 * Return value: (char *) value
 *
 */

char *LoadINI_GetString(char* p_filename, char *p_section, char *p_key, char *p_default)
{
	int i,j;

	// Check all the dictionaries to find the correct filename
	for (i=0;i<loadini_dictionaries_qty;i++) 
	{
		if (!strcmp(p_filename,loadini[i].filename))  // If the filename is correct
		{
			for (j=0;j<loadini[i].entries_qty;j++) // For each dictionary element
			{
				if (!strcmp(p_section,loadini[i].fields[j].section)
				&& !strcmp(p_key,loadini[i].fields[j].key)) // Check section and key
				{
					return ((loadini[i].fields[j].value)); // When section and key are correct then return the value
				}
			}
		}
	}
	return (p_default); // If there is not entry then return the default value
}



/*
 * long *LoadINI_GetInt(char* p_filename, char *p_section, char *p_key, char *p_default)
 *
 * This function gets an integer value from an ini file (that must be already loaded with the function LoadINI)
 *
 * Input parameters: *p_filename = Filename of the ini file already loaded
 *                   *p_section = section
 *                   *p_key = key
 *                   *p_default = default value to return if section and key were not found
 *
 * Return value: (long) value
 *
 */

long LoadINI_GetInt(char* p_filename, char *p_section, char *p_key, int p_default)
{
	char l_string[LOADINI_MAX_CHARS];
	sprintf(l_string,"%d",p_default);
	return (atol(LoadINI_GetString(p_filename, p_section, p_key, l_string))); // Use GetString to get the value and atol to convert it to integer
}



/*
 * double *LoadINI_GetFloat(char* p_filename, char *p_section, char *p_key, char *p_default)
 *
 * This function gets a float value from an ini file (that must be already loaded with the function LoadINI)
 *
 * Input parameters: *p_filename = Filename of the ini file already loaded
 *                   *p_section = section
 *                   *p_key = key
 *                   *p_default = default value to return if section and key were not found
 *
 * Return value: (double) value
 *
 */

double LoadINI_GetFloat(char* p_filename, char *p_section, char *p_key, float p_default)
{
	char l_string[LOADINI_MAX_CHARS];
	sprintf(l_string,"%f",p_default);
	return (atof(LoadINI_GetString(p_filename, p_section, p_key, l_string))); // Use GetString to get the value and atof to convert it to float 
}



/*
 * int *LoadINI_SetString(char* p_filename, char *p_section, char *p_key, char *p_value)
 *
 * This function sets a string value of a preloaded ini file array
 *
 * Input parameters: *p_filename = Filename of the ini file already loaded
 *                   *p_section = section
 *                   *p_key = key
 *                   *p_value = value
 *
 * Return value: (int) 1 = if the section and key were found and the value was updated
 *                     2 = if the section and key were not found and the value was added
 *                     0 = if the dictionary was not found and a new dictionary/section/key were created
 *
 */

int LoadINI_SetString(char* p_filename, char *p_section, char *p_key, char *p_value)
{
	int i=0,j=0,k=0;

	// Check all the dictionaries to find the correct filename
	for (i=0;i<loadini_dictionaries_qty;i++)
	{
		if (!strcmp(p_filename,loadini[i].filename)) // If the filename is correct
		{
			for (j=0;j<loadini[i].entries_qty;j++) // For each dictionary element
			{
				if (!strcmp(p_section,loadini[i].fields[j].section)
				&& !strcmp(p_key,loadini[i].fields[j].key)) // Check section and key
				{
					strcpy(loadini[i].fields[j].value,p_value); //If the element is already in the dictionary then update it with the new value
					return (1); // ...and return 1
				}
			}
						
			// If the element is not in the dictionary then we add an empty value at the end of the dictionary
			LoadINI_AddField(i,loadini[i].entries_qty,"empty","empty","empty"); // We need to allocate the memory for the new element
			loadini[i].entries_qty++; // Increase the number of entries

			// Now we must translate down all the entries until reach the wanted section
			for (j=loadini[i].entries_qty-1;j>=0;j--)  // For each dictionary element
			{
				if (!strcmp(p_section,loadini[i].fields[j].section)) // Check section
				{
					for (k=loadini[i].entries_qty-1;k>j+1;k--)
					{
						LoadINI_AddField(i,k,loadini[i].fields[k-1].section,loadini[i].fields[k-1].key,loadini[i].fields[k-1].value); // Realloc the memory and translate the element to the next position
					}
					j=k;
					break;
				}
			}

			LoadINI_AddField(i,j,p_section,p_key,p_value); // Add the new element into the correct position
			return (2);			
		}
	}

	// If there is no dictionary with that name
	LoadINI_AddField(loadini_dictionaries_qty,0,p_section,p_key,p_value); // Add the new element into a new dictionary
	strcpy(loadini[i].filename,p_filename); // Set the new dictionary name
	loadini[i].entries_qty=1; // Set the number of entries to 1
	loadini_dictionaries_qty++; // Increase the number of dictionaries
	return (0);
}



/*
 * int *LoadINI_SetInt(char* p_filename, char *p_section, char *p_key, int p_value)
 *
 * This function sets a string value of a preloaded ini file array
 *
 * Input parameters: *p_filename = Filename of the ini file already loaded
 *                   *p_section = section
 *                   *p_key = key
 *                   p_value = value
 *
 * Return value: (int) 1 = if the section and key were found and the value was updated
 *                     2 = if the section and key were not found and the value was added
 *                     0 = if the dictionary was not found and a new dictionary/section/key were created
 *
 */

int LoadINI_SetInt(char* p_filename, char *p_section, char *p_key, int p_value)
{
	char l_string[LOADINI_MAX_CHARS];
	sprintf(l_string,"%d",p_value); // Convert integer value to string
	return (LoadINI_SetString(p_filename, p_section, p_key, l_string)); // Use SetString to set the value 
}



/*
 * int *LoadINI_SetFloat(char* p_filename, char *p_section, char *p_key, float p_value)
 *
 * This function sets a string value of a preloaded ini file array
 *
 * Input parameters: *p_filename = Filename of the ini file already loaded
 *                   *p_section = section
 *                   *p_key = key
 *                   p_value = value
 *
 * Return value: (int) 1 = if the section and key were found and the value was updated
 *                     2 = if the section and key were not found and the value was added
 *                     0 = if the dictionary was not found and a new dictionary/section/key were created
 *
 */

int LoadINI_SetFloat(char* p_filename, char *p_section, char *p_key, float p_value)
{
	char l_string[LOADINI_MAX_CHARS];
	sprintf(l_string,"%f",p_value); // Convert float value to string
	return (LoadINI_SetString(p_filename, p_section, p_key, l_string)); // Use SetString to set the value
}
